package com.javass.mybatis.plugin;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.Element;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.CommentGeneratorConfiguration;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.TableConfiguration;
import org.mybatis.generator.internal.NullProgressCallback;
import org.mybatis.generator.internal.util.StringUtility;

import com.javass.utils.FreeMarkerUtils;

public class PrimaryKeyPlugin extends PluginAdapter {

	private static final String SQL_ELEMENT = "sql";
	private static final String INCLUDE_ELEMENT = "include";
	private static final String SELECT_ELEMENT = "select";
	private static final String RESULT_MAP_ELEMENT = "resultMap";
	private static final MapperCommentGenerator MAPPER_COMMENT_GENERATOR = new MapperCommentGenerator();
	private Set<String> mappers = new HashSet<String>();
	private boolean caseSensitive = false;

	private String baseURL = "";// 基础URL
	private String baseTempletePath = "templetes/base";// 模版路径
	private String permissionPrefix = "";// 权限前缀
	private String messageSuffix = "";// 国际化信息文件后缀

	@Override
	public void setContext(Context context) {
		super.setContext(context);
		// 设置默认的注释生成器
		CommentGeneratorConfiguration commentCfg = new CommentGeneratorConfiguration();
		commentCfg.setConfigurationType(MapperCommentGenerator.class.getCanonicalName());
		context.setCommentGeneratorConfiguration(commentCfg);
	}

	@Override
	public void setProperties(Properties properties) {
		super.setProperties(properties);
		String mappers = this.properties.getProperty("mappers");
		if (StringUtility.stringHasValue(mappers)) {
			for (String mapper : mappers.split(",")) {
				this.mappers.add(mapper);
			}
		} else {
			// throw new RuntimeException("Mapper插件缺少必要的mappers属性!");
		}
		String caseSensitive = this.properties.getProperty("caseSensitive");
		if (StringUtility.stringHasValue(caseSensitive)) {
			this.caseSensitive = caseSensitive.equalsIgnoreCase("TRUE");
		}
		String baseURL = this.properties.getProperty("baseURL");
		if (StringUtility.stringHasValue(baseURL)) {
			this.baseURL = baseURL;
		}
		String baseTempletePath = this.properties.getProperty("baseTempletePath");
		if (StringUtility.stringHasValue(baseTempletePath)) {
			this.baseTempletePath = baseTempletePath;
		}
		String permissionPrefix = this.properties.getProperty("permissionPrefix");
		if (StringUtility.stringHasValue(permissionPrefix)) {
			this.permissionPrefix = permissionPrefix;
			if (!this.permissionPrefix.endsWith(":")) {
				this.permissionPrefix = this.permissionPrefix + ":";
			}
		}
		String messageSuffix = this.properties.getProperty("messageSuffix");
		if (StringUtility.stringHasValue(messageSuffix)) {
			this.messageSuffix = "-" + messageSuffix;
		}
	}

	@Override
	public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
		try {
			// String daoType = introspectedTable.getDAOInterfaceType();
			// introspectedTable.setMyBatis3JavaMapperType(daoType.replace("Mapper", "Dao"));
			// introspectedTable.setDAOInterfaceType(daoType.replace("Mapper", "Dao"));
			/*
			 * <select id="getPrimaryKey" statementType="CALLABLE" parameterType="com.ccla.problem.model.Exam"> { call
			 * jsldxh.pk_generatedKey('exam_pkid',#{id,jdbcType=DECIMAL,mode=OUT}) } </select>
			 */
			String schema = introspectedTable.getTableConfiguration().getSchema();
			String tableName = introspectedTable.getTableConfiguration().getTableName();
			String domainObjectName = introspectedTable.getTableConfiguration().getDomainObjectName();
			String className = context.getJavaModelGeneratorConfiguration().getTargetPackage() + "." + domainObjectName;

			// 根节点
			XmlElement parentElement = document.getRootElement();
			// 增加自定义的主键生成测量
			// GeneratedKey generatedKey = introspectedTable.getTableConfiguration().getGeneratedKey();
			List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
			List<String> primaryKeys = new ArrayList<>();
			for (IntrospectedColumn introspectedColumn : primaryKeyColumns) {
				primaryKeys.add(introspectedColumn.getActualColumnName());
			}
			// String primaryKey = null;
			// if (primaryKeyColumns != null && primaryKeyColumns.size() > 0) {// 有主键
			// primaryKey = primaryKeyColumns.get(0).getActualColumnName();
			// 单个主键，非联合主键，提供主键的生成策略
			// if (primaryKeyColumns.size() == 1) {
			// String pkid = "";
			// if (tableName.indexOf("_") > 0) {
			// pkid = tableName.substring(tableName.indexOf("_") + 1).toLowerCase() + "_pkid";
			// }
			// XmlElement paginationPrefixElement = new XmlElement("select");
			// paginationPrefixElement.addAttribute(new Attribute("id", "generatePrimaryKey"));
			// paginationPrefixElement.addAttribute(new Attribute("statementType", "CALLABLE"));
			// paginationPrefixElement.addAttribute(new Attribute("parameterType", className));
			// paginationPrefixElement.addElement(new TextElement("{ call jsldxh.pk_generatedKey('" + pkid + "',#{"
			// + primaryKeyProperty + ",jdbcType=DECIMAL,mode=OUT}) }"));
			// parentElement.addElement(paginationPrefixElement);
			// }
			// }
			// 增加一个新的resultMap,以实体类的名称命名
			// <id column="" property="" javaType=""/>
			// <result column="" property="" javaType=""/>
			List<IntrospectedColumn> allColumns = introspectedTable.getAllColumns();
			XmlElement xmlElement = new XmlElement(RESULT_MAP_ELEMENT);
			// SimpleBeanName
			String simpleDomainObjectName = "Simple" + domainObjectName;
			xmlElement.addAttribute(new Attribute("id", simpleDomainObjectName));
			xmlElement.addAttribute(new Attribute("type", className));
			for (IntrospectedColumn column : allColumns) {
				XmlElement element = null;
				if (column.isIdentity() || primaryKeys.contains(column.getActualColumnName())) {// 主键
					element = new XmlElement("id");
				} else {
					element = new XmlElement("result");
				}
				element.addAttribute(new Attribute("column", column.getActualColumnName()));
				element.addAttribute(new Attribute("property", column.getJavaProperty()));
				element.addAttribute(new Attribute("javaType", column.getFullyQualifiedJavaType().getFullyQualifiedName()));
				if (column.getTypeHandler() != null) {
					element.addAttribute(new Attribute("typeHandler", column.getTypeHandler()));
				}
				if (column.getJdbcTypeName() != null) {
					element.addAttribute(new Attribute("jdbcType", column.getJdbcTypeName()));
				}
				xmlElement.addElement(element);
			}
			parentElement.addElement(1, xmlElement);
			// BeanName,旨在加入关联查询时使用
			xmlElement = new XmlElement(RESULT_MAP_ELEMENT);
			xmlElement.addAttribute(new Attribute("id", domainObjectName));
			xmlElement.addAttribute(new Attribute("type", className));
			xmlElement.addAttribute(new Attribute("extends", simpleDomainObjectName));
			// for (IntrospectedColumn column : allColumns) {
			// XmlElement element = null;
			// if (column.isIdentity() || primaryKeys.contains(column.getActualColumnName())) {// 主键
			// element = new XmlElement("id");
			// } else {
			// element = new XmlElement("result");
			// }
			// element.addAttribute(new Attribute("column", column.getActualColumnName()));
			// element.addAttribute(new Attribute("property", column.getJavaProperty()));
			// element.addAttribute(new Attribute("javaType",
			// column.getFullyQualifiedJavaType().getFullyQualifiedName()));
			// if (column.getTypeHandler() != null) {
			// element.addAttribute(new Attribute("typeHandler", column.getTypeHandler()));
			// }
			// if (column.getJdbcTypeName() != null) {
			// element.addAttribute(new Attribute("jdbcType", column.getJdbcTypeName()));
			// }
			// xmlElement.addElement(element);
			// }
			parentElement.addElement(0, xmlElement);

			// XmlElement element1 = new XmlElement("resultMap");
			// try {
			// PropertyUtils.copyProperties(element1, xmlElement);
			// for (Element e : element1.getElements()) {
			// XmlElement xe = (XmlElement) e;
			// if (xe.getName().equals("id")) {
			// element1.getElements().remove(xe);
			// break;
			// }
			// }
			// element1.getAttributes().add(0, new Attribute("id", "Simple" + domainObjectName));
			// parentElement.addElement(1, xmlElement);
			// } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e1) {
			// }

			// 删除
			xmlElement = new XmlElement("delete");
			xmlElement.addAttribute(new Attribute("id", "delete"));
			xmlElement.addAttribute(new Attribute("parameterType", className));
			String deleteSql = "delete from " + tableName + " where ";
			for (int i = 0; i < primaryKeyColumns.size(); i++) {
				IntrospectedColumn introspectedColumn = primaryKeyColumns.get(i);
				if (i == 0) {
					deleteSql = deleteSql
							+ String.format("%s = #{%s,jdbcType=%s}", introspectedColumn.getActualColumnName(), introspectedColumn.getJavaProperty(),
									introspectedColumn.getJdbcTypeName());
				} else {
					deleteSql = deleteSql
							+ String.format(" and %s = #{%s,jdbcType=%s}", introspectedColumn.getActualColumnName(),
									introspectedColumn.getJavaProperty(), introspectedColumn.getJdbcTypeName());
				}
			}
			xmlElement.addElement(new TextElement(deleteSql));
			parentElement.addElement(xmlElement);

			// 修改属性
			Attribute attribute = parentElement.getAttributes().get(0);
			parentElement.getAttributes().clear();
			parentElement.getAttributes().add(new Attribute(attribute.getName(), attribute.getValue().replace("Mapper", "DAO")));
			List<Element> elements = parentElement.getElements();
			XmlElement getXmlElement = null;
			for (int i = 0; i < elements.size(); i++) {
				Element element = elements.get(i);
				xmlElement = (XmlElement) element;
				// System.out.println(xmlElement.getName());
				if (xmlElement.getName().equals(SELECT_ELEMENT)) {
					List<Attribute> attributes = xmlElement.getAttributes();
					Attribute attr = null;
					for (Attribute attri : attributes) {
						if (attri.getName().equals("id") && "selectByPrimaryKey".equals(attri.getValue())) {
							attr = attri;
							break;
						}
					}
					if (attr != null) {
						// xmlElement.getAttributes().remove(attr);
						// xmlElement.getAttributes().add(0, new Attribute(attr.getName(), "get"));
						getXmlElement = new XmlElement(SELECT_ELEMENT);
						for (Attribute attri : attributes) {
							if (attr != attri || !(attri.getName().equals("id") && "selectByPrimaryKey".equals(attri.getValue()))) {
								getXmlElement.addAttribute(new Attribute(attri.getName(), attri.getValue()));
							}
						}
						getXmlElement.getAttributes().add(0, new Attribute(attr.getName(), "get"));
						getXmlElement.getElements().addAll(xmlElement.getElements());
						parentElement.addElement(getXmlElement);
						break;
					}
				}
			}
			// 增加方法
			addCommonMethod(introspectedTable, parentElement, schema, tableName, domainObjectName, primaryKeys);

			// JavaClientGeneratorConfiguration javaClientGeneratorConfiguration =
			// context.getJavaClientGeneratorConfiguration();
			String daoType = introspectedTable.getDAOInterfaceType();
			// introspectedTable.setMyBatis3JavaMapperType();
			introspectedTable.setMyBatis3FallbackSqlMapNamespace(daoType.replace("Mapper", "Dao"));

			parentElement.setName(parentElement.getName().replace("Mapper", "DAO"));

		} catch (Exception e) {
			e.printStackTrace();
		}
		return super.sqlMapDocumentGenerated(document, introspectedTable);
	}

	// 增加 一些常用的查询方法BaseResultMap
	private void addCommonMethod(IntrospectedTable introspectedTable, XmlElement rootElement, String schema, String tableName,
			String domainObjectName, List<String> primaryKeys) {
		// if (schema != null && !"".equals(schema))
		// tableName = schema + "." + tableName;
		String SimpleDomainObjectName = "Simple" + domainObjectName;
		XmlElement include = null;

		String sql = "select * from " + tableName;
		String sqlCount = "select count(1) from " + tableName;
		// 加入通用静态SQL
		XmlElement element = new XmlElement(SQL_ELEMENT);
		element.addAttribute(new Attribute("id", "countSql"));
		element.addElement(new TextElement(sqlCount));
		rootElement.addElement(element);

		element = new XmlElement(SQL_ELEMENT);
		element.addAttribute(new Attribute("id", "listSql"));
		element.addElement(new TextElement(sql));
		// include = new XmlElement(INCLUDE_ELEMENT);
		// include.addAttribute(new Attribute("refid", "orderBySql"));
		// element.addElement(include);
		rootElement.addElement(element);

		element = new XmlElement(SQL_ELEMENT);
		element.addAttribute(new Attribute("id", "pageSql"));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "GenericSQL.DialectPrefix"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "listSql"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "orderBySql"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "GenericSQL.DialectSuffix"));
		element.addElement(include);
		rootElement.addElement(element);

		// Sql : order by
		element = new XmlElement(SQL_ELEMENT);
		element.addAttribute(new Attribute("id", "orderBySql"));
		String orderBy = " order by ";
		for (int i = 0; i < primaryKeys.size(); i++) {
			if (i == 0) {
				orderBy = orderBy + primaryKeys.get(i) + " desc";
			} else {
				orderBy = orderBy + ", " + primaryKeys.get(i);
			}
		}
		element.addElement(new TextElement(orderBy));
		rootElement.addElement(element);

		// 加入通用select

		// countAll
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "countAll"));
		element.addAttribute(new Attribute("resultType", "_long"));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "countSql"));
		element.addElement(include);
		rootElement.addElement(element);

		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "countByFilter"));
		element.addAttribute(new Attribute("resultType", "_long"));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "countSql"));
		element.addElement(include);
		rootElement.addElement(element);

		// countByProperty
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "countByProperty"));
		element.addAttribute(new Attribute("resultType", "_int"));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "countSql"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "GenericSQL.whereProperty"));
		element.addElement(include);
		rootElement.addElement(element);

		// countByProperties
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "countByProperties"));
		element.addAttribute(new Attribute("resultType", "_int"));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "countSql"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "GenericSQL.whereProperties"));
		element.addElement(include);
		rootElement.addElement(element);

		// listAll listAll(page,rows)
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "listAll"));
		element.addAttribute(new Attribute(RESULT_MAP_ELEMENT, SimpleDomainObjectName));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "pageSql"));
		element.addElement(include);
		rootElement.addElement(element);

		// listAll listByFilter(page,rows)
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "pageByFilter"));
		element.addAttribute(new Attribute(RESULT_MAP_ELEMENT, SimpleDomainObjectName));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "pageSql"));
		element.addElement(include);
		rootElement.addElement(element);

		// listAll listByFilter(page,rows)
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "listByFilter"));
		element.addAttribute(new Attribute(RESULT_MAP_ELEMENT, SimpleDomainObjectName));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "pageSql"));
		element.addElement(include);
		rootElement.addElement(element);

		// List<M> listByProperty(String property, Object value);
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "listByProperty"));
		element.addAttribute(new Attribute(RESULT_MAP_ELEMENT, SimpleDomainObjectName));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "listSql"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "GenericSQL.whereProperty"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "orderBySql"));
		element.addElement(include);
		rootElement.addElement(element);

		// List<M> listByProperties(String[] properties, Object[] values);
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "listByProperties"));
		element.addAttribute(new Attribute(RESULT_MAP_ELEMENT, SimpleDomainObjectName));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "listSql"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "GenericSQL.whereProperties"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "orderBySql"));
		element.addElement(include);
		rootElement.addElement(element);

		// List<M> findAll(@Param("searchable") Searchable searchable)
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "findAll"));
		element.addAttribute(new Attribute(RESULT_MAP_ELEMENT, SimpleDomainObjectName));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "listSql"));
		element.addElement(include);
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "orderBySql"));
		element.addElement(include);
		rootElement.addElement(element);
		// long count(@Param("searchable") Searchable searchable);
		element = new XmlElement(SELECT_ELEMENT);
		element.addAttribute(new Attribute("id", "count"));
		element.addAttribute(new Attribute("resultType", "_long"));
		include = new XmlElement(INCLUDE_ELEMENT);
		include.addAttribute(new Attribute("refid", "countSql"));
		element.addElement(include);
		rootElement.addElement(element);

		//
		addSelectForFindByColumn(introspectedTable, rootElement, "code", SimpleDomainObjectName, sql);
		addSelectForFindByColumn(introspectedTable, rootElement, "name", SimpleDomainObjectName, sql);

		// 排序：resultMap -->> sql -->> insert -->> update -->> delete -->> select
		List<Element> elements = rootElement.getElements();
		List<Element> list = new ArrayList<>();
		// resultMap
		for (Element e : elements) {
			XmlElement xmlElement = (XmlElement) e;
			if (xmlElement.getName().equals(RESULT_MAP_ELEMENT)) {
				// findXmlElementAttribute(xmlElement, "type").
				List<Attribute> attributes = xmlElement.getAttributes();
				for (Attribute attr : attributes) {
					if (attr.getName().equals("id") && attr.getValue().toLowerCase().contains(domainObjectName.toLowerCase()))// 使用自定义的resultMap，mybatis-generator帮助生成的删除
						list.add(xmlElement);

				}
			}
		}
		// sql
		for (Element e : elements) {
			XmlElement xmlElement = (XmlElement) e;
			if (xmlElement.getName().equals(SQL_ELEMENT))
				list.add(xmlElement);
		}
		// insert
		for (Element e : elements) {
			XmlElement xmlElement = (XmlElement) e;
			if (xmlElement.getName().equals("insert")) {
				list.add(xmlElement);
			}
		}
		// update
		for (Element e : elements) {
			XmlElement xmlElement = (XmlElement) e;
			if (xmlElement.getName().equals("update"))
				list.add(xmlElement);
		}
		// delete
		for (Element e : elements) {
			XmlElement xmlElement = (XmlElement) e;
			if (xmlElement.getName().equals("delete"))
				list.add(xmlElement);
		}
		// select
		for (Element e : elements) {
			XmlElement xmlElement = (XmlElement) e;
			if (xmlElement.getName().equals(SELECT_ELEMENT)) {
				Attribute attribute = findXmlElementAttribute(xmlElement, RESULT_MAP_ELEMENT);
				if (attribute != null && !StringUtils.endsWithIgnoreCase(attribute.getValue(), domainObjectName)) {
					xmlElement.getAttributes().remove(attribute);
					attribute = new Attribute(RESULT_MAP_ELEMENT, SimpleDomainObjectName);
					xmlElement.getAttributes().add(attribute);
				}
				list.add(xmlElement);
			}
		}
		// 清空，重新分配
		rootElement.getElements().clear();
		for (Element e : list) {
			rootElement.addElement(e);
		}
	}

	/**
	 * 增加对于按照指定 字段/列 获取数据的查询
	 * 
	 * @param introspectedTable
	 * @param rootElement
	 * @param columnName
	 * @param SimpleDomainObjectName
	 * @param sql
	 */
	private void addSelectForFindByColumn(IntrospectedTable introspectedTable, XmlElement rootElement, String columnName,
			String SimpleDomainObjectName, String sql) {
		XmlElement element;
		IntrospectedColumn codeColumn = findColumn(introspectedTable, columnName);
		if (codeColumn != null) {
			element = new XmlElement(SELECT_ELEMENT);
			element.addAttribute(new Attribute("id", "findBy" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1)));
			element.addAttribute(new Attribute(RESULT_MAP_ELEMENT, SimpleDomainObjectName));
			element.addElement(new TextElement(String.format("%s where %s = #{%s,jdbcType=%s}", sql, codeColumn.getActualColumnName(),
					codeColumn.getJavaProperty(), codeColumn.getJdbcTypeName())));
			rootElement.addElement(element);
		}
	}

	/**
	 * 查询指定列
	 * 
	 * @param introspectedTable
	 * @param columnName
	 * @return
	 */
	private IntrospectedColumn findColumn(IntrospectedTable introspectedTable, String columnName) {
		IntrospectedColumn codeColumn = null;
		for (IntrospectedColumn column : introspectedTable.getAllColumns()) {
			if (column.getActualColumnName().equalsIgnoreCase(columnName)) {
				codeColumn = column;
				break;
			}
		}
		return codeColumn;
	}

	/**
	 * 获取指定属性
	 * 
	 * @param xmlElement
	 * @param attributeName
	 * @return
	 */
	private Attribute findXmlElementAttribute(XmlElement xmlElement, String attributeName) {
		List<Attribute> attributes = xmlElement.getAttributes();
		Attribute attribute = null;
		for (Attribute a : attributes) {
			if (a.getName().equalsIgnoreCase(attributeName)) {
				attribute = a;
				break;
			}
		}
		return attribute;
	}

	/**
	 * 生成的DAO接口
	 * 
	 * @param interfaze
	 * @param topLevelClass
	 * @param introspectedTable
	 * @return
	 */
	@Override
	public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		// IntrospectedTableMyBatis3Impl batis3Impl = (IntrospectedTableMyBatis3Impl) introspectedTable;
		// ProgressCallback progressCallback = new NullProgressCallback();
		// List<String> warnings = new ArrayList<>();
		// batis3Impl.calculateGenerators(warnings, progressCallback);

		// 获取实体类
		FullyQualifiedJavaType entityType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
		// entityType.getPrimitiveTypeWrapper().
		// import接口
		List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
		String pkType = (primaryKeyColumns.size() > 1 ? introspectedTable.getPrimaryKeyType() : primaryKeyColumns.get(0).getFullyQualifiedJavaType()
				.getFullyQualifiedName());
		for (String mapper : mappers) {
			interfaze.addImportedType(new FullyQualifiedJavaType(mapper));
			interfaze.getSuperInterfaceTypes().clear();
			interfaze.addSuperInterface(new FullyQualifiedJavaType(mapper + "<" + entityType.getShortName() + "," + pkType + ">"));
		}
		// interfaze.getType().getPrimitiveTypeWrapper()
		// import实体类
		interfaze.addImportedType(entityType);
		MAPPER_COMMENT_GENERATOR.addClassComment(interfaze, introspectedTable);
		return super.clientGenerated(interfaze, topLevelClass, introspectedTable);
	}

	/**
	 * 处理实体类的包和@Table注解
	 * 
	 * @param topLevelClass
	 * @param introspectedTable
	 */
	private void processEntityClass(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		// 引入JPA注解
		topLevelClass.addImportedType("javax.persistence.*");
		// topLevelClass.addImportedType("com.ccla.constant.TableGeneratorConstants");
		// List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
		// if (primaryKeyColumns.size() == 1 && "DECIMAL".equalsIgnoreCase(primaryKeyColumns.get(0).getJdbcTypeName()))
		// {//
		// 单个主键，非联合主键，提供主键的生成策略
		// topLevelClass.addImportedType("com.javass.common.jpa.annotation.generator.CallableGenerator");
		// topLevelClass.addImportedType("com.javass.common.jpa.annotation.generator.CallableParameter");
		// topLevelClass.addImportedType("com.javass.common.jpa.annotation.generator.CallableParameterType");
		// topLevelClass.addImportedType("java.sql.Types");
		// }
		String tableName = introspectedTable.getFullyQualifiedTableNameAtRuntime();
		// 如果包含空格，或者需要分隔符，需要完善
		if (StringUtility.stringContainsSpace(tableName)) {
			tableName = context.getBeginningDelimiter() + tableName + context.getEndingDelimiter();
		}
		// 增加Entity
		topLevelClass.addAnnotation("@Entity");
		// 是否忽略大小写，对于区分大小写的数据库，会有用
		if (caseSensitive && !topLevelClass.getType().getShortName().equals(tableName)) {
			topLevelClass.addAnnotation("@Table(name = \"" + tableName + "\")");
		} else if (!topLevelClass.getType().getShortName().equalsIgnoreCase(tableName)) {
			topLevelClass.addAnnotation("@Table(name = \"" + tableName + "\")");
		}
		// introspectedTable.getContext().getCommentGenerator()
		// topLevelClass.addFileCommentLine("/**注释*/");
	}

	/**
	 * 生成 页面 及 messages.properties 国际化资源文件
	 * 
	 * @author zhuguojie，zhu915139481@126.com
	 * @version 1.0
	 * @param introspectedTable
	 */
	private void buildPage(IntrospectedTable introspectedTable) {
		TableConfiguration configuration = introspectedTable.getTableConfiguration();
		String domainObjectName = configuration.getDomainObjectName();
		introspectedTable.getAllColumns();

		Map<String, Object> dataMap = new HashMap<>();
		dataMap.put("table", introspectedTable);
		dataMap.put("primaryKeyColumns", introspectedTable.getPrimaryKeyColumns());
		dataMap.put("allColumns", introspectedTable.getAllColumns());
		dataMap.put("domainObjectName", domainObjectName);
		dataMap.put("baseURL", baseURL);
		dataMap.put("permissionPrefix", this.permissionPrefix);
		dataMap.put("remarks", MAPPER_COMMENT_GENERATOR.getTableRemarks(introspectedTable));
		setPrimariKey(introspectedTable, dataMap);
		// src/main/javaResources资源文件存放路径
		String targetProject = context.getSqlMapGeneratorConfiguration().getTargetProject();
		String basePath = targetProject + File.separator + "jsp";
		// 当前类对应的页面存放路径
		String resourcePath = basePath + File.separator + domainObjectName.toLowerCase();

		try {
			FreeMarkerUtils.remove();
			String listTemplate = FreeMarkerUtils.template(baseTempletePath, "list.ftl", dataMap);
			FileUtils.writeStringToFile(new File(resourcePath, "list.jsp"), listTemplate);
			String editFormTemplate = FreeMarkerUtils.template(baseTempletePath, "editForm.ftl", dataMap);
			FileUtils.writeStringToFile(new File(resourcePath, "editForm.jsp"), editFormTemplate);
			String queryTemplate = FreeMarkerUtils.template(baseTempletePath, "query.ftl", dataMap);
			FileUtils.writeStringToFile(new File(resourcePath, "query.jsp"), queryTemplate);
		} catch (IOException e) {
			e.printStackTrace();
		}
		// 写入国际化资源文件
		try {
			FreeMarkerUtils.remove();
			// if (StringUtils.isNotBlank(this.permissionPrefix)) {
			// suffix = this.permissionPrefix.replace(":", "-").substring(0, this.permissionPrefix.length() - 1);
			// }
			String messageTemplate = FreeMarkerUtils.template("templetes/message", "message.ftl", dataMap);
			FileUtils.writeStringToFile(new File(targetProject + File.separator + "message", "message" + messageSuffix + "_zh.properties"),
					messageTemplate, true);
			String validationTemplate = FreeMarkerUtils.template("templetes/message", "validation.ftl", dataMap);
			FileUtils.writeStringToFile(new File(targetProject + File.separator + "message", "validation" + messageSuffix + "_zh.properties"),
					validationTemplate, true);
		} catch (IOException e) {
			e.printStackTrace();
		}
		// 构建Service和Controller的java源码文件
		buildJavaSource(introspectedTable);
	}

	/**
	 * 构建Service和Controller的java源码文件
	 * 
	 * @description
	 * @author zhuguojie，zhu915139481@126.com
	 * @datetime 2015年11月13日 下午1:58:09
	 * @version 1.0
	 * @param introspectedTable
	 * @param domainObjectName
	 * @param dataMap
	 */
	private void buildJavaSource(IntrospectedTable introspectedTable) {
		TableConfiguration configuration = introspectedTable.getTableConfiguration();
		String domainObjectName = configuration.getDomainObjectName();
		introspectedTable.getAllColumns();

		Map<String, Object> dataMap = new HashMap<>();
		dataMap.put("table", introspectedTable);
		dataMap.put("primaryKeyColumns", introspectedTable.getPrimaryKeyColumns());
		dataMap.put("allColumns", introspectedTable.getAllColumns());
		dataMap.put("domainObjectName", domainObjectName);
		dataMap.put("baseURL", baseURL);
		dataMap.put("permissionPrefix", this.permissionPrefix);
		dataMap.put("remarks", MAPPER_COMMENT_GENERATOR.getTableRemarks(introspectedTable));
		// 生成对应的Service 和 Controller
		String baseSrcPath = context.getJavaClientGeneratorConfiguration().getTargetProject();
		String baseSrcPackage = context.getJavaClientGeneratorConfiguration().getTargetPackage();
		baseSrcPackage = baseSrcPackage.substring(0, baseSrcPackage.lastIndexOf(".") + 1);

		String servicePackage = baseSrcPackage + "service";
		String controllerPackage = baseSrcPackage + "web.controller";
		try {
			dataMap.put("date", new Date());

			dataMap.put("permissionPrefix", permissionPrefix);

			dataMap.put("modelFullName", context.getJavaModelGeneratorConfiguration().getTargetPackage() + "." + domainObjectName);
			setPrimariKey(introspectedTable, dataMap);
			dataMap.put("serviceFullName", servicePackage + "." + domainObjectName + "Service");
			dataMap.put("serviceSimpleName", domainObjectName + "Service");
			dataMap.put("DAOFullName", introspectedTable.getDAOInterfaceType());
			dataMap.put("DAOSimpleName", domainObjectName + "DAO");

			// Service
			dataMap.put("package", servicePackage);
			FreeMarkerUtils.remove();
			String serviceTemplate = FreeMarkerUtils.template("templetes/source1", "service.ftl", dataMap);
			FileUtils.writeStringToFile(new File(baseSrcPath + File.separator + servicePackage.replace(".", File.separator), domainObjectName
					+ "Service.java"), serviceTemplate);
			// ServiceImpl
			dataMap.put("package", servicePackage);
			FreeMarkerUtils.remove();
			serviceTemplate = FreeMarkerUtils.template("templetes/source1", "service-impl.ftl", dataMap);
			FileUtils.writeStringToFile(new File(baseSrcPath + File.separator + (servicePackage + ".impl.").replace(".", File.separator),
					domainObjectName + "ServiceImpl.java"), serviceTemplate);
			// Controller
			dataMap.put("package", controllerPackage);
			FreeMarkerUtils.remove();
			String controllerTemplate = FreeMarkerUtils.template("templetes/source1", "controller.ftl", dataMap);
			FileUtils.writeStringToFile(new File(baseSrcPath + File.separator + controllerPackage.replace(".", File.separator), domainObjectName
					+ "Controller.java"), controllerTemplate);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 加入主键信息
	 * 
	 * @author zhuguojie，zhu915139481@126.com <br>
	 *         2016年2月25日 下午2:33:29
	 * @version 1.0
	 * @param introspectedTable
	 * @param dataMap
	 */
	public void setPrimariKey(IntrospectedTable introspectedTable, Map<String, Object> dataMap) {
		if (introspectedTable.getPrimaryKeyColumns().size() == 1) {
			dataMap.put("primaryKeyName", introspectedTable.getPrimaryKeyColumns().get(0).getFullyQualifiedJavaType().getShortName());
			dataMap.put("isSimpleKey", true);
		} else {
			String primaryKeyType = introspectedTable.getPrimaryKeyType();
			dataMap.put("primaryKeyName", primaryKeyType.substring(primaryKeyType.lastIndexOf(".") + 1));
			dataMap.put("primaryKeyType", primaryKeyType);
			dataMap.put("isSimpleKey", false);
		}
	}

	/**
	 * 生成基础实体类
	 * 
	 * @param topLevelClass
	 * @param introspectedTable
	 * @return
	 */
	@Override
	public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		processEntityClass(topLevelClass, introspectedTable);
		MAPPER_COMMENT_GENERATOR.addClassComment(topLevelClass, introspectedTable);
		buildPage(introspectedTable);
		addExtMethod(topLevelClass);
		return true;
	}

	/**
	 * 增加额外的一些方法
	 * 
	 * @author zhuguojie，zhu915139481@126.com <br>
	 *         2016年3月2日 上午11:56:15
	 * @version 1.0
	 * @param topLevelClass
	 */
	private void addExtMethod(TopLevelClass topLevelClass) {
		// 上传init方法
		Method method = new Method();
		method.setName("init");
		method.setVisibility(JavaVisibility.PUBLIC);
		method.addBodyLine("");

		method.addJavaDocLine("/**");
		method.addJavaDocLine(" * 信息初始化");
		method.addJavaDocLine(" * @author zhuguojie，zhu915139481@126.com");
		method.addJavaDocLine(" * @date " + DateFormatUtils.format(new Date(), "yyyy年MM月dd日 "));
		method.addJavaDocLine(" * @version 1.0");
		method.addJavaDocLine(" */");
		// topLevelClass.addMethod(method);
		topLevelClass.getMethods().add(0, method);
	}

	/**
	 * 生成实体类注解KEY对象
	 * 
	 * @param topLevelClass
	 * @param introspectedTable
	 * @return
	 */
	@Override
	public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		processEntityClass(topLevelClass, introspectedTable);
		MAPPER_COMMENT_GENERATOR.addModelComment(topLevelClass, introspectedTable, true);
		return true;
	}

	/**
	 * 生成带BLOB字段的对象
	 * 
	 * @param topLevelClass
	 * @param introspectedTable
	 * @return
	 */
	@Override
	public boolean modelRecordWithBLOBsClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		processEntityClass(topLevelClass, introspectedTable);
		return false;
	}

	/**
	 * 字段生成
	 */
	@Override
	public boolean modelFieldGenerated(Field field, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn,
			IntrospectedTable introspectedTable, ModelClassType modelClassType) {
		if (field.getType().getFullyQualifiedName().equals(Date.class.getSimpleName())) {
			topLevelClass.addImportedType("com.fasterxml.jackson.annotation.JsonFormat");
			field.addAnnotation("@JsonFormat(pattern = \"yyyy-MM-dd\")");
		}
		return super.modelFieldGenerated(field, topLevelClass, introspectedColumn, introspectedTable, modelClassType);
	}

	class MyProgressCallback extends NullProgressCallback {

	}

	@Override
	public boolean clientSelectAllMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return super.clientSelectAllMethodGenerated(method, interfaze, introspectedTable);
	}

	@Override
	public boolean clientSelectAllMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return super.clientSelectAllMethodGenerated(method, topLevelClass, introspectedTable);
	}

	@Override
	public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles() {
		List<GeneratedJavaFile> files = super.contextGenerateAdditionalJavaFiles();
		return files;
	}

	@Override
	public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
		List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles = super.contextGenerateAdditionalJavaFiles(introspectedTable);
		return contextGenerateAdditionalJavaFiles;
	}

	@Override
	public boolean validate(List<String> warnings) {
		return true;
	}

}
